/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is lutsko.com code.
 *
 * The Initial Developer of the Original Code is  James Lutsko 
 * (jim@lutsko.com http://www.lutsko.com).
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the NPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

package autoschedule;

import java.util.Iterator;
import java.util.Arrays;
import java.util.Set;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Hashtable;
import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.Comparator;
import java.util.Collections;

public class League implements Serializable
{
    String name;
    Integer teams;
    Vector allTeams = null;
    Vector interLeagueTeams = new Vector();

    Hashtable facilityCapacity = null;
    Hashtable facilityPref = null;
    Vector games = null;
    
    Double hrsPerGame;
    Double gamesPerWeek; 
    Integer gamesPerTeam; 

    public Double getHrsPerGame(){ return hrsPerGame;}
    public Double getGamesPerWeek(){ return gamesPerWeek;}
    public Integer getGamesPerTeam(){ return gamesPerTeam;}
    public Integer getTeams(){ return teams;}
    public String getName(){ return name;}

    public Vector getAllTeams()
    {
	if(allTeams == null) {allTeams = new Vector(); teams = new Integer(1); allTeams.add(new Team(0,this));}
	return (Vector) allTeams.clone();
    }
    public Vector getInterLeagueTeams()
    {
	if(interLeagueTeams == null) {interLeagueTeams = new Vector();}
	return (Vector) interLeagueTeams.clone();
    }

    public void setHrsPerGame(Double var){ hrsPerGame = var;}
    public void setGamesPerWeek(Double var){ gamesPerWeek = var;}
    public void setGamesPerTeam(Integer var){ gamesPerTeam = var;}
    public void setTeams(Integer var){ teams = var;}
    public void setName(String var){ name = var;}

    public int getNumberOfTeams(){return (allTeams == null ? 0 : allTeams.size());}
    public Team getTeam(int j){return (Team) (allTeams.get(j));}

    Integer getFacilityPref(int j, AutoScheduleDoc doc){return (Integer) facilityPref.get(doc.facilities.get(j));}
    Integer getFacilityCap(int j, AutoScheduleDoc doc){return (Integer) facilityCapacity.get(doc.facilities.get(j));}

    public League(String _name)
    {
	name = _name; 
	teams = new Integer(0); 
	hrsPerGame = new Double(1);
	gamesPerWeek = new Double(1);
	gamesPerTeam = new Integer(10);
	facilityCapacity = new Hashtable();
	facilityPref = new Hashtable();
	games = new Vector();
    }

    //    public void removeGame(int r){ games.removeElementAt(r);}
    public void removeGame(Game g){ games.removeElement(g);}

    public int getDuration(){ return (int) (60*hrsPerGame.doubleValue());}

    public void createGames()
    {
	int numTeams = teams.intValue();
	if(numTeams%2 == 1) numTeams++;
	AutoSchedule a = new AutoSchedule(numTeams);
	int rank = 0;
	if(games == null) games = new Vector();
	else games.clear();
	//Next, create the teams
	if(allTeams == null) allTeams = new Vector();
	else allTeams.clear();
	for(int i=0;i<teams.intValue();i++)
	    allTeams.add(new Team(i,this));

	for(Iterator I = a.Matches.iterator();I.hasNext();)
	    {
		int[] m = (int[]) I.next();
		rank++;
		for(int j=0;j<m.length;j++)
		    {
			if(j == allTeams.size()) continue; // byes
			if(m[j] >= 0 && m[j] != allTeams.size())
			    {								
				Game g = new Game((Team) (allTeams.get(j)),(Team) (allTeams.get(m[j])),rank,this);
				m[m[j]] = -1;
				games.add(g);
			    }
		    }
	    }
    }

    public int getNumberOfGames(){return games.size();}
    public Game getGame(int r){ return (Game) games.get(r);}

    public Integer possibilities()
    {
	int p = 0;
	for(Enumeration keys = facilityCapacity.keys(); keys.hasMoreElements();) 
	    {
		Facility f = (Facility) keys.nextElement();
		Integer cap = (Integer) facilityCapacity.get(f);
		p += cap.intValue();
	    }
	return new Integer(p);
    }

    public void clearSchedule()
    {
	games.clear();
	allTeams.clear();
    }


    public void schedule(AutoScheduleDoc doc)
    {
	// First, we need a list of facilities in order of preference

	for(Enumeration keys = facilityPref.keys(); keys.hasMoreElements();) 
	    {
		Facility f = (Facility) keys.nextElement();
		Integer pref = (Integer) facilityPref.get(f);
		f.rank = pref;
	    }
	Object[] keys = facilityPref.keySet().toArray();
	Arrays.sort(keys);

	System.out.println("League: " + name);
	
	for(int i=0;i<keys.length;i++)
	    {
		Facility f = (Facility) keys[i];
		System.out.println("Facility: " + f.getName() + " rank = " + f.rank);
	    } 

	// A. add games until every team has the required number
	//
	// 1. How many games does each team have?
	//
	for(Iterator I = allTeams.iterator();I.hasNext();)
	    ((Team) I.next()).totalGames = 0;


	for(Iterator I = games.iterator();I.hasNext();)
	    {
		Game g = (Game) I.next();
		g.incrementTotalGames();
	    }


	// 2. While the minimum number of games of any team in the leage
	// is less than the required number, loop over the master list
	// of games and add more
	//
	int max = games.size();
	int min = 10000;
	for(Iterator I = allTeams.iterator();I.hasNext();)
	    min = Math.min(((Team) I.next()).totalGames,min);

	boolean flag = true;
	while(min < gamesPerTeam.intValue() && flag)
	    {
		flag = false;
		for(int i=0;i<max;i++)
		    {
			Game g = (Game) games.get(i);
			if((g.getTeam(1).totalGames < gamesPerTeam.intValue())
			   && (g.getTeam(2).totalGames < gamesPerTeam.intValue()))
			    {
				games.add(new Game(g));
				flag = true;
				g.incrementTotalGames();
			    }
		    }
		min = 10000;
		for(Iterator I = allTeams.iterator();I.hasNext();)
		    min = Math.min(((Team) I.next()).totalGames,min);
	    }

	//B. Now we can schedule
	//

	int duration = (int) (60*hrsPerGame.doubleValue());
	for(Iterator I = games.iterator();I.hasNext();)
	    {
		Game g = (Game) I.next();

		Game g1 = g.getTeam(1).lastGame();
		Game g2 = g.getTeam(2).lastGame();

		// What was the most recent game day of either of these teams?
		//
		Date mostRecentGame = null;
		if(g1 != null) mostRecentGame = g1.getDate();
		if(g2 != null && g2.getDate() != null 
		   && (mostRecentGame == null || g2.getDate().after(mostRecentGame))) mostRecentGame = g2.getDate();

		// Earliest viable date is 1 day after mostRecentGame
		//
		int wait = 5;
		if(g.getTeam(1).gamesPerWeek() < gamesPerWeek.doubleValue()
		   && g.getTeam(2).gamesPerWeek() < gamesPerWeek.doubleValue())
		    wait = 1;
		
		Calendar c = Calendar.getInstance();
		
		while(wait > 0)
		    {
			Date earliestDate = null;
			if(mostRecentGame != null)
			    {
				c.setTime(mostRecentGame);
				c.add(c.DATE,wait);			
				earliestDate = c.getTime();
			    }
			
			// Find earliest block consistant with this date
			// and with sufficient capacity
			//
			
			int facilityNumber = -1;
			Day bestDay = null;
			for(int i=0;i<keys.length;i++)
			    {
				Facility f = (Facility) keys[i];
				Integer simulGames = (Integer) facilityCapacity.get(f);
				if(simulGames.intValue() <= 0) continue; // cann't play here
				Day day = f.getEarliestBlock(duration,earliestDate,simulGames);
								
				// Keep the higher ranked facility if its only a question
				// of start times: for tied facility ranks, use earliest
				if(day == null) continue;
				
				if(bestDay == null || 
				   (((Facility) keys[facilityNumber]).rank == f.rank ?
				    (day.compareTo(bestDay) < 0) : day.before(bestDay)))
				    {
					bestDay = day; 
					facilityNumber = i;
				    }
			    }
			if(facilityNumber > -1)
			    {
				Facility f = (Facility) keys[facilityNumber];
				Integer simulGames = (Integer) facilityCapacity.get(f);
				f.schedule(g,bestDay,duration,simulGames,doc);
				wait = 0;
			    }
			if(wait == 5) wait = 1;
			else if(wait == 1) wait = 0;
		    }
		g.assign();
	    }
    }

    public String toString(){return name;}
    public void sortGames() 
    {
	Collections.sort(games, new Comparator() {
		public int compare(Object o1, Object o2) {
		    if(o1 == o2) return 0;
		    Game g1 = (Game) o1;
		    Game g2 = (Game) o2;
		    
		    if(g1 == g2) return 0;
		    if(g2 == null) return 1;
		    else if(g1 == null) return -1;

		    int d1 = g1.getDayOfYear();
		    int d2 = g2.getDayOfYear();
		    
		    int ret = (d1 < d2 ? -1 : (d1 == d2 ? 0 : 1)); 
		    if(ret == 0 && g1.time != null && g2.time != null)
			ret = g1.time.compareTo(g2.time);
		    if(ret == 0 && g1.time == null && g2.time != null) return 1;
		    if(ret == 0 && g1.time != null && g2.time == null) return -1;
		    if(ret == 0)
			{
			    if(g1.getFacilityName() == g2.getFacilityName()) return 0;
			    if(g1.getFacilityName() == null) return -1;
			    if(g2.getFacilityName() == null) return 1;
			    ret = g1.getFacilityName().compareTo(g2.getFacilityName());
			}

		    return ret;
		}});
    }

    static final long serialVersionUID = 6082790181074385519L;
}
